/*******************************************************************************
 * Package: com.song.flowable.service.impl
 * Type:    FlowableServiceImpl
 * Date:    2022-02-05 22:18
 *
 * Copyright (c) 2022 HUANENG GUICHENG TRUST CORP.,LTD All Rights Reserved.
 *
 * You may not use this file except in compliance with the License.
 *******************************************************************************/
package com.song.flowable.service.impl;

import com.song.flowable.convert.TaskConvert;
import com.song.flowable.dto.*;
import com.song.flowable.entity.ActRuTask;
import com.song.flowable.entity.DoneEntity;
import com.song.flowable.mapper.FlowableMapper;
import com.song.flowable.mapper.IActRuTaskMapper;
import com.song.flowable.service.FlowableService;

import com.song.flowable.vo.ReturnTaskVo;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;

import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能描述：
 *
 * @author Songxianyang
 * @date 2022-02-05 22:18
 */
@Service
public class FlowableServiceImpl implements FlowableService {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;

    @Resource
    private FlowableMapper flowableMapper;
    @Resource
    private IActRuTaskMapper iActRuTaskMapper;

    @Override
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        List<Task> task = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        String InstanceId = pi.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    @Override
    public String createDeployment(DeploymentDTO dto) {
        Deployment deploy = repositoryService.createDeployment().addClasspathResource(dto.getResourceBpmnPath())
                .name(dto.getDeploymentFlowName()).deploy();
        return "流程部署成功,部署的实例id为：" + deploy.getId();
    }

    @Override
    public List<ProcessDefinition> getProcessDefinitionList(DeploymentDTO dto) {
        if (Objects.nonNull(dto.getPage()) || Objects.nonNull(dto.getPageSize())) {
            List<ProcessDefinition> pages = repositoryService.createProcessDefinitionQuery().processDefinitionKey(dto.getProcessDefinitionKey())
                    .listPage(dto.getPage(), dto.getPageSize());
            return pages;
        }
        List<ProcessDefinition> list = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey(dto.getProcessDefinitionKey())
                .list();
        return list;
    }

    @Override
    public String startFlowable(StartFlowableDTO dto) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(dto.getProcessDefinitionKey(),
                dto.getMap());
        return "启动流程成功，流程实例ID为：" + processInstance.getId();
    }

    @Override
    public List<Execution> executions(StartFlowableDTO dto) {
        //分页
        if (Objects.nonNull(dto.getPage()) || Objects.nonNull(dto.getPageSize())) {
            List<Execution> executionPages = runtimeService.createExecutionQuery().processDefinitionKey(dto.getProcessDefinitionKey())
                    .listPage(dto.getPage(), dto.getPageSize());
            return executionPages;
        }
        //不分页
        List<Execution> executions = runtimeService.createExecutionQuery().processDefinitionKey(dto.getProcessDefinitionKey()).list();
        return executions;
    }

    @Override
    public List<ReturnTaskVo> todoList(TodoDTO dto) {
        List<String> ids;
        List<Task> list;
        if (Objects.nonNull(dto.getPage()) || Objects.nonNull(dto.getPageSize())) {
            //分页
            list = taskService.createTaskQuery().taskAssignee(dto.getUserId()).orderByTaskCreateTime().desc()
                    .listPage(dto.getPage(), dto.getPageSize());
        } else {
            //不分页
            list = taskService.createTaskQuery().taskAssignee(dto.getUserId()).orderByTaskCreateTime().desc().list();

        }
        if (CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }
        ids = list.stream().map(Task::getId).collect(Collectors.toList());
        List<ActRuTask> actRuTasks = iActRuTaskMapper.selectBatchIds(ids);
        Map<String, String> nodeMap = actRuTasks.stream().collect(Collectors.toMap(ActRuTask::getId, ActRuTask::getTaskDefKey));
        List<ReturnTaskVo> returnTaskVos = TaskConvert.INSTANCE.toConvertTaskVoList(list);
        returnTaskVos.forEach(returnTaskVo -> {
            returnTaskVo.setNodeId(nodeMap.get(returnTaskVo.getTaskId()));
        });
        return returnTaskVos;
    }

    @Override
    public List<DoneEntity> doneList(DoneDTO dto) {
        List<DoneEntity> list = flowableMapper.doneByUserId(dto.getUserId());
        return list;
    }

    @Override
    public String acceptOrReject(AcceptOrRejectDTO dto) {
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
        //领取任务
        taskService.claim(task.getId(), dto.getUserId());
        // TODO 一般企业级会签开发基本上都是在前台送用户过来（然而这个用户都是通过角色去查询得到得）拿到代码根据自己的实际优化此接口。SteveCode只是想做通用才这么做的
        //会签时 入参String转List
        Map<String, Object> map = dto.getMap();
        if (Objects.nonNull(map)) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String value = (String) entry.getValue();
                String[] values = value.split(",");
                if (values.length > 1) {
                    entry.setValue(Arrays.asList(values));
                }
            }
        }

        // 完成
        taskService.complete(task.getId(), dto.getMap());
        return "流程执行成功！";
    }

    @Override
    public List<Task> tasks(String group) {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(group).list();
        return tasks;
    }

    @Override
    public List<HistoricActivityInstance> historicActivityInstances(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).finished().orderByHistoricActivityInstanceEndTime().asc().list();
        return historicActivityInstances;
    }

    @Override
    public String currentTask(String taskId, String targetTaskKey) {
        Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (currentTask == null) {
            return "节点不存在";
        }
        List<String> key = new ArrayList<>();
        key.add(currentTask.getTaskDefinitionKey());
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(key, targetTaskKey)
                .changeState();
        return "驳回成功...";
    }

    @Override
    public String deleteProcessInstanceById(String processInstanceId, String reason) {
        runtimeService.deleteProcessInstance(processInstanceId, reason);
        return "终止流程实例成功";
    }

    @Override
    public String handUpProcessInstance(String processInstanceId) {
        runtimeService.suspendProcessInstanceById(processInstanceId);
        return "挂起流程成功...";
    }

    @Override
    public String activateProcessInstance(String processInstanceId) {
        runtimeService.activateProcessInstanceById(processInstanceId);
        return "恢复流程成功...";
    }

    @Override
    public Boolean isExistProcIntRunning(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            return false;
        }
        return true;
    }

    @Override
    public List<HistoricProcessInstance> getMyStartProcint(String userId) {
        List<HistoricProcessInstance> list = historyService
                .createHistoricProcessInstanceQuery()
                .startedBy(userId)
                .orderByProcessInstanceStartTime()
                .asc()
                .list();
        return list;
    }

    /**
     * 多实例加签
     * activityId – 当前会签流程节点id
     * parentExecutionId – 可以是流程实例 ID，
     * executionVariables – 在新创建的多实例执行中设置为局部变量的变量，并设置加签用户
     *
     * @return String
     */
    @Override
    @Transactional
    public String addMultiInstanceExecution(AddMultiInstanceDTO addMultiInstance) {
        if (Objects.isNull(addMultiInstance)) {
            return "入参对象不能为空！";
        }
        // 支持一次加多个人
        List<String> addLabelUserIds = addMultiInstance.getAddLabelUserId();
        addLabelUserIds.forEach(user->{
            Map<String, Object> executionVariables = new HashMap<>(1);
            executionVariables.put("assignee", user);
            runtimeService.addMultiInstanceExecution(addMultiInstance.getActivityId(), addMultiInstance.getProcessInstanceId(), executionVariables);
        });
        return "加签成功，请如下用户：" + addMultiInstance.getAddLabelUserId() + "先进行审批";
    }

    @Override
    public String deleteMultiInstanceExecution(String executionId) {

        runtimeService.deleteMultiInstanceExecution(executionId, true);
        return "减签成功";
    }

    @Override
    public List<ReturnTaskVo> signTaskUsers(SignTaskUserDTO dto) {
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(dto.getProcessInstanceId())
                .taskName(dto.getNodeName()).list();
        if (CollectionUtils.isEmpty(tasks)) {
            return Collections.EMPTY_LIST;
        }

        List<String> ids = tasks.stream().map(Task::getId).collect(Collectors.toList());
        List<ActRuTask> actRuTasks = iActRuTaskMapper.selectBatchIds(ids);
        Map<String, String> nodeMap = actRuTasks.stream().collect(Collectors.toMap(ActRuTask::getId, ActRuTask::getTaskDefKey));
        List<ReturnTaskVo> returnTaskVos = TaskConvert.INSTANCE.toConvertTaskVoList(tasks);
        returnTaskVos.forEach(returnTaskVo -> {
            returnTaskVo.setNodeId(nodeMap.get(returnTaskVo.getTaskId()));
        });
        return returnTaskVos;
    }

}
